linux内存占用分析之meminfo

您所在的位置:网站首页 linux 内存排行 linux内存占用分析之meminfo

linux内存占用分析之meminfo

2023-06-28 04:50| 来源: 网络整理| 查看: 265

一、背景

近期在公司的某台linux虚拟机上,发现内存几乎消耗殆尽,但找不到其去向。在调查过程中,重点分析了/proc/meminfo文件,对其内存占用进行了学习与分析。

特记录在此,与君分享。

参考资料:http://linuxperf.com/?cat=7 二、环境 虚拟机OS : CentOS Linux release 7.4.1708 (Core) 虚拟机平台 : VMWare 三、问题描述

通过free -h或top查看内存消耗,发现used已接近最大可用内存,但各进程常驻内存(RES)远比used要小。

先摆出结论:

在VMWare虚拟机平台上,宿主机可以通过一个叫Balloon driver(vmware_balloon module)的驱动程序模拟客户机linux内部的进程占用内存,被占用的内存实际上是被宿主机调度到其他客户机去了。但这种驱动程序模拟的客户机进程在linux上的内存动态分配并没有被linux内核统计进来,于是造成了上述问题的现象。

3.1 top 结果

按内存消耗排序,取消耗大于0的部分

top - 16:46:45 up 8 days, 10:25, 1 user, load average: 0.00, 0.01, 0.05 Tasks: 109 total, 1 running, 108 sleeping, 0 stopped, 0 zombie %Cpu(s): 0.1 us, 0.0 sy, 0.0 ni, 99.9 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st KiB Mem : 7994080 total, 185776 free, 7625996 used, 182308 buff/cache KiB Swap: 4157436 total, 294944 free, 3862492 used. 115964 avail Mem PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND DATA 3725 root 20 0 9057140 1.734g 5020 S 0.3 22.7 367:48.86 java 8882680 1087 mysql 20 0 2672240 233064 1076 S 0.0 2.9 102:33.71 mysqld 2596840 496 root 20 0 36828 3512 3388 S 0.0 0.0 0:31.13 systemd-journal 356 14564 root 20 0 145700 2424 1148 S 0.0 0.0 0:02.94 sshd 924 1 root 20 0 128164 2404 724 S 0.0 0.0 1:02.18 systemd 84628 14713 root 20 0 157716 2204 1512 R 0.0 0.0 0:00.08 top 1176 14568 root 20 0 115524 1784 1272 S 0.0 0.0 0:00.59 bash 632 687 root 20 0 305408 1548 1168 S 0.0 0.0 13:59.34 vmtoolsd 75352 676 root 20 0 216388 1240 872 S 0.0 0.0 1:56.69 rsyslogd 148768 682 root 20 0 472296 908 160 S 0.0 0.0 1:06.73 NetworkManager 222852 684 root 20 0 24336 752 444 S 0.0 0.0 0:22.19 systemd-logind 504 690 polkitd 20 0 534132 560 220 S 0.0 0.0 0:07.34 polkitd 450080 677 dbus 20 0 32772 460 128 S 0.0 0.0 0:08.34 dbus-daemon 8900 688 root 20 0 21620 452 296 S 0.0 0.0 4:42.68 irqbalance 488 698 root 20 0 126232 432 328 S 0.0 0.0 0:30.25 crond 1312 922 root 20 0 562392 412 28 S 0.0 0.0 4:52.69 tuned 304472 924 root 20 0 105996 188 92 S 0.0 0.0 0:03.64 sshd 760 653 root 16 -4 55452 84 0 S 0.0 0.0 0:08.81 auditd 8664 532 root 20 0 46684 4 4 S 0.0 0.0 0:02.81 systemd-udevd 1916 705 root 20 0 110044 4 4 S 0.0 0.0 0:00.02 agetty 344 3.2 top结果第四行内存总体使用情况 属性 大小(G) 说明 total 7.6 可分配内存总计值 used 7.26 已分配内存 free 0.17 未分配内存 buff/cache 0.17 buff与缓存 各属性满足公式:total = used + free + buff/cache used在该linux版本(centos7)上,已经反映实际分配的内存,不需要再去除buff/cache部分 3.3 top进程列表内存相关列统计 列 合计(G) 说明 VIRT 14.236 进程申请的虚拟内存大小,申请不意味着分配,该值与实际内存消耗关系不大。 RES 1.9747 进程常驻内存,包含进程间共享内存。 SHR 0.0171 进程间共享内存,该值是推算出来的,存在误差,意义不大。 3.4 问题来了

RES合计值比used少了5G多!这些内存哪去了?

理论上,各进程的RES合计值因为会重复计算共享内存,应该比used值略大。实际上这两个值也往往是接近的,不应该差这么多。 四、清查linux内存消耗

为了进一步检查linux中内存消耗的去向,需要对/proc/meminfo文件进行一次彻底的分析统计。

linux上各种内存查看工具如free,top实际上都是从/proc下面找linux内核的各种统计文件。 4.1 /proc/meminfo内容 MemTotal: 7994080 kB MemFree: 125256 kB MemAvailable: 932412 kB Buffers: 8 kB Cached: 993796 kB SwapCached: 252 kB Active: 1182220 kB Inactive: 1213960 kB Active(anon): 693796 kB Inactive(anon): 717156 kB Active(file): 488424 kB Inactive(file): 496804 kB Unevictable: 0 kB Mlocked: 0 kB SwapTotal: 4157436 kB SwapFree: 4157172 kB Dirty: 8 kB Writeback: 0 kB AnonPages: 1402140 kB Mapped: 41584 kB Shmem: 8576 kB Slab: 143220 kB SReclaimable: 86720 kB SUnreclaim: 56500 kB KernelStack: 5360 kB PageTables: 7184 kB NFS_Unstable: 0 kB Bounce: 0 kB WritebackTmp: 0 kB CommitLimit: 8154476 kB Committed_AS: 2073776 kB VmallocTotal: 34359738367 kB VmallocUsed: 191584 kB VmallocChunk: 34359310332 kB HardwareCorrupted: 0 kB AnonHugePages: 1284096 kB HugePages_Total: 0 HugePages_Free: 0 HugePages_Rsvd: 0 HugePages_Surp: 0 Hugepagesize: 2048 kB DirectMap4k: 89920 kB DirectMap2M: 4104192 kB DirectMap1G: 6291456 kB 4.2 meminfo内容分析 属性 大小(k) 说明 扩展说明 MemTotal: 7994080 可供linux内核分配的内存总量。 比物理内存总量少一点,因为主板/固件会保留一部分内存、linux内核自己也会占用一部分内存。 MemFree: 125256 表示系统尚未分配的内存。 MemAvailable: 932412 当前可用内存。 MemFree只是尚未分配的内存,并不是所有可用的内存。有些已经分配掉的内存是可以回收再分配的。比如cache/buffer、slab都有一部分是可以回收的,这部分可回收的内存加上MemFree才是系统可用的内存,即MemAvailable。同时要注意,MemAvailable是内核使用特定的算法估算出来的,并不精确。 Buffers: 8 块设备(block device)所占用的特殊file-backed pages,包括:直接读写块设备,以及文件系统元数据(metadata)比如superblock使用的缓存页。 Buffers内存页同时也在LRU list中,被统计在Active(file)或Inactive(file)之中。 Cached: 993796 所有file-backed pages 用户进程的内存页分为两种:file-backed pages(与文件对应的内存页),和anonymous pages(匿名页),比如进程的代码、映射的文件都是file-backed,而进程的堆、栈都是不与文件相对应的、就属于匿名页。file-backed pages在内存不足的时候可以直接写回对应的硬盘文件里,称为page-out,不需要用到交换区(swap);而anonymous pages在内存不足时就只能写到硬盘上的交换区(swap)里,称为swap-out。 SwapCached: 252 SwapCached包含的是被确定要swap-out,但是尚未写入交换区的匿名内存页。 SwapCached内存页会同时被统计在LRU或AnonPages或Shmem中,它本身并不占用额外的内存。 Active: 1182220 active包含active anon和active file LRU是一种内存页回收算法,Least Recently Used,最近最少使用。LRU认为,在最近时间段内被访问的数据在以后被再次访问的概率,要高于最近一直没被访问的页面。于是近期未被访问到的页面就成为了页面回收的第一选择。Linux kernel会记录每个页面的近期访问次数,然后设计了两种LRU list: active list 和 inactive list, 刚访问过的页面放进active list,长时间未访问过的页面放进inactive list,回收内存页时,直接找inactive list即可。另外,内核线程kswapd会周期性地把active list中符合条件的页面移到inactive list中。 Inactive: 1213960 inactive包含inactive anon和inactive file Active(anon): 693796 活跃匿名页,anonymous pages(匿名页)。 Inactive(anon): 717156 非活跃匿名页 Active(file): 488424 活跃文件内存页 Inactive(file): 496804 非活跃文件内存页 Unevictable: 0 因为种种原因无法回收(page-out)或者交换到swap(swap-out)的内存页 Unevictable LRU list上是不能pageout/swapout的内存页,包括VM_LOCKED的内存页、SHM_LOCK的共享内存页(同时被统计在Mlocked中)、和ramfs。在unevictable list出现之前,这些内存页都在Active/Inactive lists上,vmscan每次都要扫过它们,但是又不能把它们pageout/swapout,这在大内存的系统上会严重影响性能,unevictable list的初衷就是避免这种情况的发生。 Mlocked: 0 被系统调用"mlock()"锁定到内存中的页面。Mlocked页面是不可收回的。 被锁定的内存因为不能pageout/swapout,会从Active/Inactive LRU list移到Unevictable LRU list上。Mlocked与以下统计项重叠:LRU Unevictable,AnonPages,Shmem,Mapped等。 SwapTotal: 4157436 swap空间总计 SwapFree: 4157172 当前剩余swap Dirty: 8 需要写入磁盘的内存页的大小 Dirty并不包括系统中全部的dirty pages,需要再加上另外两项:NFS_Unstable 和 Writeback,NFS_Unstable是发给NFS server但尚未写入硬盘的缓存页,Writeback是正准备回写硬盘的缓存页。 Writeback: 0 正在被写回的内存页的大小 AnonPages: 1402140 Anonymous pages(匿名页)数量 + AnonHugePages(透明大页)数量 进程所占的内存页分为anonymous pages和file-backed pages,理论上,所有进程的PSS之和 = Mapped + AnonPages。PSS是Proportional Set Size,每个进程实际使用的物理内存(比例分配共享库占用的内存),可以在/proc/[1-9]*/smaps中查看。 Mapped: 41584 正被用户进程关联的file-backed pages Cached包含了所有file-backed pages,其中有些文件当前不在使用,但Cached仍然可能保留着它们的file-backed pages;而另一些文件正被用户进程关联,比如shared libraries、可执行程序的文件、mmap的文件等,这些文件的缓存页就称为mapped。 Shmem: 8576 Shmem统计的内容包括:1.shared memory;2.tmpfs和devtmpfs。所有tmpfs类型的文件系统占用的空间都计入共享内存,devtmpfs是/dev文件系统的类型,/dev/下所有的文件占用的空间也属于共享内存。可以用ls和du命令查看。如果文件在没有关闭的情况下被删除,空间仍然不会释放,shmem不会减小,可以用 lsof -a +L1 / 命令列出这样的文件。 shared memory被视为基于tmpfs文件系统的内存页,既然基于文件系统,就不算匿名页,所以不被计入/proc/meminfo中的AnonPages,而是被统计进了:Cached或Mapped(当shmem被attached时候)。然而它们背后并不存在真正的硬盘文件,一旦内存不足的时候,它们是需要交换区才能swap-out的,所以在LRU lists里,它们被放在Inactive(anon) 或 Active(anon)或 unevictable (如果被locked的话)里。注意:/proc/meminfo中的 Shmem 统计的是已经分配的大小,而不是创建时申请的大小。 Slab: 143220 通过slab分配的内存,Slab=SReclaimable+SUnreclaim slab是linux内核的一种内存分配器。linux内核的动态内存分配有以下几种方式:1.alloc_pages/__get_free_page:以页为单位分配。2.vmalloc:以字节为单位分配虚拟地址连续的内存块。3.slab:对小对象进行分配,不用为每个小对象分配一个页,节省了空间;内核中一些小对象创建析构很频繁,Slab对这些小对象做缓存,可以重复利用一些相同的对象,减少内存分配次数。4.kmalloc:以slab为基础,以字节为单位分配物理地址连续的内存块。 SReclaimable: 86720 slab中可回收的部分。 SUnreclaim: 56500 slab中不可回收的部分。 KernelStack: 5360 给用户线程分配的内核栈消耗的内存页 每一个用户线程都会分配一个kernel stack(内核栈),内核栈虽然属于线程,但用户态的代码不能访问,只有通过系统调用(syscall)、自陷(trap)或异常(exception)进入内核态的时候才会用到,也就是说内核栈是给kernel code使用的。在x86系统上Linux的内核栈大小是固定的8K或16K。Kernel stack(内核栈)是常驻内存的,既不包括在LRU lists里,也不包括在进程的RSS/PSS内存里。RSS是Resident Set Size 实际使用物理内存(包含共享库占用的内存),可以在/proc/[1-9]*/smaps中查看。 PageTables: 7184 Page Table的消耗的内存页 Page Table的用途是翻译虚拟地址和物理地址,它是会动态变化的,要从MemTotal中消耗内存。 NFS_Unstable: 0 发给NFS server但尚未写入硬盘的缓存页 Bounce: 0 bounce buffering消耗的内存页 有些老设备只能访问低端内存,比如16M以下的内存,当应用程序发出一个I/O 请求,DMA的目的地址却是高端内存时(比如在16M以上),内核将在低端内存中分配一个临时buffer作为跳转,把位于高端内存的缓存数据复制到此处。 WritebackTmp: 0 正准备回写硬盘的缓存页 CommitLimit: 8154476 overcommit阈值,CommitLimit = (Physical RAM * vm.overcommit_ratio / 100) + Swap Linux是允许memory overcommit的,即承诺给进程的内存大小超过了实际可用的内存。commit(或overcommit)针对的是内存申请,内存申请不等于内存分配,内存只在实际用到的时候才分配。但可以申请的内存有个上限阈值,即CommitLimit,超出以后就不能再申请了。 Committed_AS: 2073776 所有进程已经申请的内存总大小 VmallocTotal: 34359738367 可分配的虚拟内存总计 VmallocUsed: 191584 已通过vmalloc分配的内存,不止包括了分配的物理内存,还统计了VM_IOREMAP、VM_MAP等操作的值 VM_IOREMAP是把IO地址映射到内核空间、并未消耗物理内存 VmallocChunk: 34359310332 通过vmalloc可分配的虚拟地址连续的最大内存 HardwareCorrupted: 0 因为内存的硬件故障而删除的内存页 AnonHugePages: 1284096 AnonHugePages统计的是Transparent HugePages (THP),THP与Hugepages不是一回事,区别很大。Hugepages在/proc/meminfo中是被独立统计的,与其它统计项不重叠,既不计入进程的RSS/PSS中,又不计入LRU Active/Inactive,也不会计入cache/buffer。如果进程使用了Hugepages,它的RSS/PSS不会增加。而AnonHugePages完全不同,它与/proc/meminfo的其他统计项有重叠,首先它被包含在AnonPages之中,而且在/proc//smaps中也有单个进程的统计,与进程的RSS/PSS是有重叠的,如果用户进程用到了THP,进程的RSS/PSS也会相应增加,这与Hugepages是不同的。 Transparent Huge Pages 缩写 THP ,这个是 RHEL 6 开始引入的一个功能,在 Linux6 上透明大页是默认启用的。由于 Huge pages 很难手动管理,而且通常需要对代码进行重大的更改才能有效的使用,因此 RHEL 6 开始引入了 Transparent Huge Pages ( THP ), THP 是一个抽象层,能够自动创建、管理和使用传统大页。THP 为系统管理员和开发人员减少了很多使用传统大页的复杂性 , 因为 THP 的目标是改进性能 , 因此其它开发人员 ( 来自社区和红帽 ) 已在各种系统、配置、应用程序和负载中对 THP 进行了测试和优化。这样可让 THP 的默认设置改进大多数系统配置性能。但是 , 不建议对数据库工作负载使用 THP 。这两者最大的区别在于 : 标准大页管理是预分配的方式,而透明大页管理则是动态分配的方式。 HugePages_Total: 0 预分配的可使用的标准大页池的大小。HugePages在内核中独立管理,只要一经定义,无论是否被使用,都不再属于free memory。 Huge pages(标准大页) 是从 Linux Kernel 2.6 后被引入的,目的是通过使用大页内存来取代传统的 4kb 内存页面, 以适应越来越大的系统内存,让操作系统可以支持现代硬件架构的大页面容量功能。 HugePages_Free: 0 标准大页池中尚未分配的标准大页 HugePages_Rsvd: 0 用户程序预申请的标准大页,尚未真的分配走 HugePages_Surp: 0 标准大页池的盈余 Hugepagesize: 2048 标准大页大小,这里是2M DirectMap4k: 89920 映射为4kB的内存数量 DirectMap所统计的不是关于内存的使用,而是一个反映TLB效率的指标。TLB(Translation Lookaside Buffer)是位于CPU上的缓存,用于将内存的虚拟地址翻译成物理地址,由于TLB的大小有限,不能缓存的地址就需要访问内存里的page table来进行翻译,速度慢很多。为了尽可能地将地址放进TLB缓存,新的CPU硬件支持比4k更大的页面从而达到减少地址数量的目的, 比如2MB,4MB,甚至1GB的内存页,视不同的硬件而定。所以DirectMap其实是一个反映TLB效率的指标。 DirectMap2M: 4104192 映射为2MB的内存数量 DirectMap1G: 6291456 映射为1GB的内存数量 4.3 根据meminfo统计内存占用

linux上的内存消耗总的来说有两部分,一部分是内核kernel进程,另一部分是用户进程。因此我们统计这两部分进程的内存消耗再相加即可。

内核部分:Slab+ VmallocUsed + PageTables + KernelStack + HardwareCorrupted + Bounce + X X是指linux内核没有统计进来的,动态内存分配中通过alloc_pages分配的内存。

http://linuxperf.com/?cat=7就指出了一个这样的例子:

在VMware guest上有一个常见问题,就是VMWare ESX宿主机会通过guest上的Balloon driver(vmware_balloon module)占用guest的内存,有时占用得太多会导致guest无内存可用,这时去检查guest的/proc/meminfo只看见MemFree很少、但看不出内存的去向,原因就是Balloon driver通过alloc_pages分配内存,没有在/proc/meminfo中留下统计值,所以很难追踪。

用户进程部分:Active + Inactive + Unevictable + (HugePages_Total * Hugepagesize) 或 Cached + AnonPages + Buffers + (HugePages_Total * Hugepagesize)

根据上述公式,除掉X部分,得出linux内核统计出来的已分配内存为:2.62G。

该值远小于使用free或top得到的used。这里推测原因就是linux没有统计进来的alloc_pages分配的内存。考虑到该linux确实是在VMWare平台上申请的虚拟机,因此我们推测是由于虚拟机平台内存不足,于是宿主机模拟该客户机内部进程消耗内存,实际上将内存调度到其他虚客户机去了。 五、结论

虚拟机管理员尚未最终确认,目前仅仅是推测

在VMWare虚拟机平台上,宿主机可以通过一个叫Balloon driver(vmware_balloon module)的驱动程序模拟客户机linux内部的进程占用内存,被占用的内存实际上是被宿主机调度到其他客户机去了。但这种驱动程序模拟的客户机进程在客户机linux上是通过alloc_pages实现的内存动态分配,并没有被linux内核统计进来,于是造成了内存去向不明的现象。



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3